#include <jni.h>
#include <string>
#include "Reader.h"

JavaVM *jvm;

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_connect(JNIEnv *env, jobject thiz, jstring ip, jint port) {
    Reader *reader = new Reader();
    jobject obj;
    obj = env->NewGlobalRef(thiz);
    reader->thiz = obj;
    reader->jvm = jvm;
    const char *_ip = env->GetStringUTFChars(ip, 0);
    if (!reader->connect(_ip, port)) {
        delete reader;
        return RFID_FAIL;
    }

    jlong handle = (jlong) reader;
    jclass clazz = env->GetObjectClass(obj);
    jfieldID handle_fieldID = env->GetFieldID(clazz, "handle", "J");
    env->SetLongField(obj, handle_fieldID, handle);

    jfieldID ip_fieldID = env->GetFieldID(clazz, "ip", "Ljava/lang/String;");
    env->SetObjectField(obj, ip_fieldID, ip);

    jclass cls_string = env->FindClass("java/lang/String");
    reader->cls_string = reinterpret_cast<jclass> (env->NewGlobalRef(cls_string));

    jstring ascii = env->NewStringUTF("ASCII");
    reader->ascii = reinterpret_cast<jstring> (env->NewGlobalRef(ascii));

    return RFID_SUCCESS;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_disconnect(JNIEnv *env, jobject thiz) {
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID handle_fieldID = env->GetFieldID(clazz, "handle", "J");
    jlong handle = env->GetLongField(thiz, handle_fieldID);
    if (handle == 0) {
        return RFID_FAIL;
    }
    Reader *reader = (Reader *) handle;
    reader->disconnect();
    env->DeleteGlobalRef(reader->thiz);
    env->DeleteGlobalRef(reader->cls_string);
    env->DeleteGlobalRef(reader->ascii);
    env->SetLongField(thiz, handle_fieldID, (jlong) 0);
    delete reader;
    return RFID_SUCCESS;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_inventory(JNIEnv *env, jobject thiz, jint times) {
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID handle_filedID = env->GetFieldID(clazz, "handle", "J");
    jlong handle = env->GetLongField(thiz, handle_filedID);
    if (handle == 0) {
        return RFID_FAIL;
    }
    Reader *reader = (Reader *) handle;
    return reader->inventory(times);
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_light(JNIEnv *env, jobject thiz, jbyteArray epc, jint len) {
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID handle_filedID = env->GetFieldID(clazz, "handle", "J");
    jlong handle = env->GetLongField(thiz, handle_filedID);
    if (handle == 0) {
        return RFID_FAIL;
    }
    Reader *reader = (Reader *) handle;
    unsigned char _epc[len];
    env->GetByteArrayRegion(epc, 0, len, (jbyte *) _epc);
    env->DeleteLocalRef(epc);
    return reader->light(_epc, len);
}

struct ANT_GETTER {
    jmethodID get_power;
    jmethodID get_enabled;
    jmethodID get_work;
    jmethodID get_sleep;
};

struct ANT_SETTER {
    jmethodID init;
    jmethodID set_power;
    jmethodID set_enabled;
    jmethodID set_work;
    jmethodID set_sleep;
};

struct FREQ_GETTER {
    jmethodID get_status;
    jmethodID get_region;
    jmethodID get_mhz;
    jmethodID get_khz;
    jmethodID get_step;
    jmethodID get_channels;
};

struct FREQ_SETTER {
    jmethodID init;
    jmethodID set_status;
    jmethodID set_region;
    jmethodID set_mhz;
    jmethodID set_khz;
    jmethodID set_step;
    jmethodID set_channels;
};

ANT_GETTER get_ant_getter(JNIEnv *env) {
    jclass cls = env->FindClass("com/example/sixteen/jt/lib/JTAnt");
    ANT_GETTER getter;
    getter.get_power = env->GetMethodID(cls, "getPower", "()I");
    getter.get_enabled = env->GetMethodID(cls, "getEnabled", "()I");
    getter.get_work = env->GetMethodID(cls, "getWork", "()I");
    getter.get_sleep = env->GetMethodID(cls, "getSleep", "()I");
    return getter;
}

ANT_SETTER get_ant_setter(JNIEnv *env, jclass cls) {
    ANT_SETTER setter;
    setter.init = env->GetMethodID(cls, "<init>", "()V");
    setter.set_power = env->GetMethodID(cls, "setPower", "(I)V");
    setter.set_enabled = env->GetMethodID(cls, "setEnabled", "(I)V");
    setter.set_work = env->GetMethodID(cls, "setWork", "(I)V");
    setter.set_sleep = env->GetMethodID(cls, "setSleep", "(I)V");
    return setter;
}

FREQ_GETTER get_freq_getter(JNIEnv *env) {
    jclass cls = env->FindClass("com/example/sixteen/jt/lib/JTFreq");
    FREQ_GETTER getter;
    getter.get_status = env->GetMethodID(cls, "getStatus", "()I");
    getter.get_region = env->GetMethodID(cls, "getRegion", "()I");
    getter.get_mhz = env->GetMethodID(cls, "getMHz", "()I");
    getter.get_khz = env->GetMethodID(cls, "getKHz", "()I");
    getter.get_step = env->GetMethodID(cls, "getStep", "()I");
    getter.get_channels = env->GetMethodID(cls, "getChannels", "()I");
    return getter;
}

FREQ_SETTER get_freq_setter(JNIEnv *env) {
    jclass cls = env->FindClass("com/example/sixteen/jt/lib/JTFreq");
    FREQ_SETTER setter;
    setter.init = env->GetMethodID(cls, "<init>", "()V");
    setter.set_status = env->GetMethodID(cls, "setStatus", "(I)V");
    setter.set_region = env->GetMethodID(cls, "setRegion", "(I)V");
    setter.set_mhz = env->GetMethodID(cls, "setMHz", "(I)V");
    setter.set_khz = env->GetMethodID(cls, "setKHz", "(I)V");
    setter.set_step = env->GetMethodID(cls, "setStep", "(I)V");
    setter.set_channels = env->GetMethodID(cls, "setChannels", "(I)V");
    return setter;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_getAnt(JNIEnv *env, jobject thiz, jobjectArray ants) {
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID handle_filedID = env->GetFieldID(clazz, "handle", "J");
    jlong handle = env->GetLongField(thiz, handle_filedID);
    if (handle == 0) {
        return RFID_FAIL;
    }

    Reader *reader = (Reader *) handle;
    JT_ANT_16 ant;
    memset(&ant, 0, sizeof(JT_ANT_16));
    if (reader->get_ant(ant) != RFID_SUCCESS) {
        return RFID_FAIL;
    }

    jclass cls = env->FindClass("com/example/sixteen/jt/lib/JTAnt");
    ANT_SETTER setter = get_ant_setter(env, cls);
    for (int i = 0; i < ant.count; i++) {
        jobject obj_ant = env->NewObject(cls, setter.init);
        env->CallVoidMethod(obj_ant, setter.set_power, ant.ants[i].power);
        env->CallVoidMethod(obj_ant, setter.set_enabled, (ant.enable >> i) & 1);
        env->CallVoidMethod(obj_ant, setter.set_work, htons(ant.ants[i].work_ms));
        env->CallVoidMethod(obj_ant, setter.set_sleep, htons(ant.ants[i].sleep_ms));
        env->SetObjectArrayElement(ants, i, obj_ant);
    }
    return RFID_SUCCESS;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_setAnt(JNIEnv *env, jobject thiz, jobjectArray ants) {
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID handle_filedID = env->GetFieldID(clazz, "handle", "J");
    jlong handle = env->GetLongField(thiz, handle_filedID);
    if (handle == 0) {
        return RFID_FAIL;
    }

    Reader *reader = (Reader *) handle;
    JT_ANT_16 ant;
    memset(&ant, 0, sizeof(JT_ANT_16));
    ant.store = 0;
    ant.count = 16;
    unsigned short enabled = 0;

    ANT_GETTER getter = get_ant_getter(env);
    int count = env->GetArrayLength(ants);
    if (count == 0) return RFID_FAIL;
    for (int i = 0; i < count; i++) {
        jobject obj_ant = env->GetObjectArrayElement(ants, i);
        if (obj_ant == NULL) {
            perror("ant not set");
            return RFID_FAIL;
        }
        int enable = env->CallIntMethod(obj_ant, getter.get_enabled);
        int power = env->CallIntMethod(obj_ant, getter.get_power);
        int work = env->CallIntMethod(obj_ant, getter.get_work);
        int sleep = env->CallIntMethod(obj_ant, getter.get_sleep);
        if (enable == 1) {
            enabled += (1 << i);
        }
        ant.ants[i].idx = i + 1;
        ant.ants[i].power = power;
        ant.ants[i].work_ms = htons(work);
        ant.ants[i].sleep_ms = htons(sleep);
    }
    ant.enable = enabled;
    return reader->set_ant(ant);
}


struct SEL_MASK_GETTER {
    jmethodID init;
    jmethodID get_ant;
    jmethodID get_channels;
    jmethodID get_mask;
    jmethodID get_len;
};

SEL_MASK_GETTER get_sel_mask(JNIEnv *env, jclass cls) {
    SEL_MASK_GETTER setter;
    setter.init = env->GetMethodID(cls, "<init>", "()V");
    setter.get_ant = env->GetMethodID(cls, "getAnt", "()I");
    setter.get_channels = env->GetMethodID(cls, "getChannels", "()I");
    setter.get_len = env->GetMethodID(cls, "getLen", "()I");
    setter.get_mask = env->GetMethodID(cls, "getMark", "()[B");
    return setter;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_startLightMulti(JNIEnv *env, jobject thiz,
                                                       jobjectArray marks, jint times) {
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID handle_filedID = env->GetFieldID(clazz, "handle", "J");
    jlong handle = env->GetLongField(thiz, handle_filedID);
    if (handle == 0) {
        return RFID_FAIL;
    }
    jclass cls = env->FindClass("com/example/sixteen/jt/lib/SelMark");
    if (cls == NULL) {
        return RFID_FAIL;
    }
    Reader *reader = (Reader *) handle;
    SEL_MASK_GETTER getter = get_sel_mask(env, cls);
    int count = env->GetArrayLength(marks);
    if (count == 0) return RFID_FAIL;
    std::list<SEL_MASK> list;
    for (int i = 0; i < count; i++) {
        SEL_MASK sel_mask;
        memset(&sel_mask, 0, sizeof(SEL_MASK));

        jobject obj = env->GetObjectArrayElement(marks, i);
        sel_mask.ant = env->CallIntMethod(obj, getter.get_ant);
        sel_mask.channels = env->CallIntMethod(obj, getter.get_channels);
        sel_mask.len = env->CallIntMethod(obj, getter.get_len);
        jbyteArray array = (jbyteArray) env->CallObjectMethod(obj, getter.get_mask);
        int len = env->GetArrayLength(array);
        if (len > 12) continue;
        env->GetByteArrayRegion(array, 0, len, (jbyte *) sel_mask.mask);
        env->DeleteLocalRef(array);
        list.push_back(sel_mask);
    }
    return reader->start_light_multi(list, times);
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_setFreq(JNIEnv *env, jobject thiz, jobject freq) {
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID handle_filedID = env->GetFieldID(clazz, "handle", "J");
    jlong handle = env->GetLongField(thiz, handle_filedID);
    if (handle == 0) {
        return RFID_FAIL;
    }

    Reader *reader = (Reader *) handle;
    JT_FRQ jt_freq;
    memset(&jt_freq, 0, sizeof(JT_FRQ));
    FREQ_GETTER getter = get_freq_getter(env);
    int status = env->CallIntMethod(freq, getter.get_status);
    int region = env->CallIntMethod(freq, getter.get_region);
    int mhz = env->CallIntMethod(freq, getter.get_mhz);
    int khz = env->CallIntMethod(freq, getter.get_khz);
    int step = env->CallIntMethod(freq, getter.get_step);
    int channels = env->CallIntMethod(freq, getter.get_channels);
    jt_freq.status = status;
    jt_freq.region = region;
    jt_freq.MHZ = htons(mhz);
    jt_freq.KHz = htons(khz);
    jt_freq.step = htons(step);
    jt_freq.channels = channels;
    return reader->set_freq(jt_freq);
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_getFreq(JNIEnv *env, jobject thiz, jobject freq) {
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID handle_filedID = env->GetFieldID(clazz, "handle", "J");
    jlong handle = env->GetLongField(thiz, handle_filedID);
    if (handle == 0) {
        return RFID_FAIL;
    }

    Reader *reader = (Reader *) handle;
    JT_FRQ jt_freq;
    memset(&jt_freq, 0, sizeof(JT_FRQ));
    if (reader->get_freq(jt_freq) != RFID_SUCCESS) {
        return RFID_FAIL;
    }

    FREQ_SETTER setter = get_freq_setter(env);
    env->CallVoidMethod(freq, setter.set_status, jt_freq.status);
    env->CallVoidMethod(freq, setter.set_region, jt_freq.region);
    env->CallVoidMethod(freq, setter.set_mhz, htons(jt_freq.MHZ));
    env->CallVoidMethod(freq, setter.set_khz, htons(jt_freq.KHz));
    env->CallVoidMethod(freq, setter.set_step, htons(jt_freq.step));
    env->CallVoidMethod(freq, setter.set_channels, jt_freq.channels);
    return RFID_SUCCESS;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_sixteen_jt_lib_Reader_stopLightMulti(JNIEnv *env, jobject thiz) {
    jclass clazz = env->GetObjectClass(thiz);
    jfieldID handle_filedID = env->GetFieldID(clazz, "handle", "J");
    jlong handle = env->GetLongField(thiz, handle_filedID);
    if (handle == 0) {
        return RFID_FAIL;
    }
    Reader *reader = (Reader *) handle;
    return reader->stop_light_multi();
}

extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    jvm = vm;
    return JNI_VERSION_1_6;
}